import {message} from 'antd';
import * as puzzlecardsService from "@/service/puzzlecards";

const delay = (millisecond) => {
  return new Promise((resolve) => {
    setTimeout(resolve, millisecond);
  });
};

export default {
  // model 唯一标识
  namespace: 'puzzlecards',
  // 传入组件的数据
  state: {
    data: [],
    counter: 0,
  },
  // effect 充当了这么一个中间层，当 action 被 dispatch 之后，会先到达 effect 处理副作用
  // 然后该 effect 最终会促使新的 action 发送出去，这个新的 action 可能被其他的 effect 再捕获继续处理
  // 也可能被 reducer 捕获并结束，无论怎样，最终处理逻辑的终点都将是 reducer。
  // 对于视图层来讲，其实并不会感知 effect 和 reducer 的区别
  // 视图层只是通过 action 描述想做什么，至于这个 action 之后是直接被 reducer 处理还是通过 effect 再到 reducer
  // 视图层并不感知，也不应该关心。这样我们就做到了数据逻辑和视图逻辑的分离处理。
  effects: {
    /*
      getData: function* ({ payload }, { call, put }) {
        const data = yield call(SomeService.getEndpointData, payload, 'maybeSomeOtherParams');
        yield put({ type: 'getData_success', payload: data });
      }
      入参有两个对象：
        1.第一个对象就是匹配这个 effect 的 action 对象，因此可以取到约定的 payload 这个字段
        2.第二个对象是 effect 原语集，其中 call, put 最为常用
        3.generator function 入参中的两个对象都是在运行时由 dva 注入到 generator function 中的
      call 其实是一个函数，和 yield 关键字配合使用处理异步逻辑：
        1.call 第一个参数是一个函数，要求函数返回 Promise
        2.之后的参数是该函数调用时的入参
      yield call 调用后就阻塞了，Promise 被解析后，得到异步调用的结果，存储到 data 中，然后程序才能继续进行

      执行 reducers ，不需要 namespace 前缀：
      看到下面一行又执行了 put，put 也是一个函数，put 和 yield 配合使用，
      用来派发一个 action，和 dispatch 的功能 一模一样！只不过是在 effect 函数中使用而已。
     */
    * queryInitCards(_, {call, put}) {
      try { // 加入 try catch 捕获抛错
        //获取服务端数据
        const puzzle = yield call(puzzlecardsService.random_joke);
        //添加一个卡片数据，这个会触发 reducer 的执行
        yield put({type: 'addNewCard', payload: puzzle});

        //暂停 3 s.
        yield call(delay, 3000);

        //第二次获取服务端数据
        const puzzle2 = yield call(puzzlecardsService.random_joke);
        //再添加一个卡片数据。这个又会触发 reducer 的执行
        yield put({type: 'addNewCard', payload: puzzle2});
      } catch (e) {
        message.error('数据获取失败'); // 打印错误信息
      }
    },
  },
  // 组件可使用 dispatch 调用以下方法
  // 返回值作为新的 state，返回值必须是一个新构造对象，绝不能把旧 state 的引用返回！
  // dva 注入了旧的 state 和 action 中的 payload
  reducers: {
    addNewCard(state, {payload: newCard}) {
      const nextCounter = state.counter + 1;
      const newCardWithId = {...newCard, id: nextCounter};
      //新构造对象
      //concat 方法不会改变现有的数组，而仅仅会返回被连接数组的一个副本。
      const nextData = state.data.concat(newCardWithId);
      return {
        data: nextData,
        counter: nextCounter,
      };
    }
  },
}

/*
总过程reducers：
1. 页面事件触发 dispatch(action)
2. action 到达 reducer，第二个参数作用
3. reducers 改变 state 后重新注入组件
4. 触发页面的重新渲染，第一个参数作用

总过程effects：
1. 页面事件触发 dispatch(action)
2. action 到达 effects 中间件
3. 异步处理
  3.1. 异步处理后继续 dispatch action 到 reducer
  3.2. 其他异步处理继续 dispatch action 到 reducer
4. reducers 改变 state 后重新注入组件
5. 改变的 state 触发页面的重新渲染
 */
